package me.kagami.gsp;

import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiTypesUtil;
import com.intellij.util.IncorrectOperationException;
import me.kagami.gsp.service.GenerateContext;
import me.kagami.gsp.service.GenerateDefaultForOneMethodDeclare;
import me.kagami.gsp.util.PsiClassUtils;
import me.kagami.gsp.util.PsiDocumentUtils;
import me.kagami.gsp.util.PsiToolUtils;
import org.jetbrains.annotations.NotNull;

public abstract class BaseGenericsSetterAction extends PsiElementBaseIntentionAction {

    @Override
    public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement element) throws IncorrectOperationException {
        PsiElement psiParent = PsiTreeUtil.getParentOfType(element, PsiLocalVariable.class, PsiMethod.class);
        if (psiParent == null) {
            return;
        }
        if (psiParent instanceof PsiLocalVariable) {
            PsiLocalVariable psiLocal = (PsiLocalVariable) psiParent;
            if (!(psiLocal.getParent() instanceof PsiDeclarationStatement)) {
                return;
            }
            handleWithLocalVariable(psiLocal, project, psiLocal);
        }
    }

    private void handleWithLocalVariable(PsiLocalVariable localVariable, Project project, PsiElement element) {
        PsiElement parent = localVariable.getParent();
        if (!(parent instanceof PsiDeclarationStatement)) {
            return;
        }

        PsiClass psiClass = PsiTypesUtil.getPsiClass(localVariable.getType());

        PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(project);
        PsiFile containingFile = element.getContainingFile();
        Document document = psiDocumentManager.getDocument(containingFile);
        //获取代码前的空格
        String splitText = PsiToolUtils.calculateSplitText(document, parent.getTextOffset());

        GenerateContext generateContext = new GenerateContext(localVariable, project, psiClass, splitText);
        StringBuilder builder = generateContext.getBuilder();
        builder.append(splitText);
        for (PsiMethod method : PsiClassUtils.extractSetMethods(psiClass)) {
            generateContext.setMethod(method);
            GenerateDefaultForOneMethodDeclare temp = new GenerateDefaultForOneMethodDeclare();
            temp.setSimpleValue(isSimpleValue());
            temp.setGenerateContext(generateContext);
            temp.generateMethodArgsDeclareStatement();
            temp.generateDefaultForOneMethod();
            builder.append(splitText);
        }


        document.insertString(parent.getTextOffset() + parent.getText().length(), builder.toString());
        PsiDocumentUtils.commitAndSaveDocument(psiDocumentManager, document);
        PsiToolUtils.addImportToFile(psiDocumentManager, (PsiJavaFile) containingFile, document, generateContext.getNewImportList());
    }

    protected abstract boolean isSimpleValue();


    @Override
    public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement element) {
        return isValidAsLocalVariableWithSetterOrGetterMethod(element, true);
    }

    @Override
    public @NotNull
    String getFamilyName() {
        return "Generate all setter";
    }



    @NotNull
    private Boolean isValidAsLocalVariableWithSetterOrGetterMethod(@NotNull PsiElement element, boolean setter) {
        PsiClass psiClass = getLocalVariableContainingClass(element);
        if (psiClass == null) {
            return false;
        }
        if (setter) {
            return PsiClassUtils.checkClassHasValidSetMethod(psiClass);
        } else {
            return PsiClassUtils.checkClasHasValidGetMethod(psiClass);
        }
    }

    public static PsiClass getLocalVariableContainingClass(@NotNull PsiElement element) {
        PsiElement psiParent = PsiTreeUtil.getParentOfType(element, PsiLocalVariable.class);
        if (psiParent == null) {
            return null;
        }
        PsiLocalVariable psiLocal = (PsiLocalVariable) psiParent;
        if (!(psiLocal.getParent() instanceof PsiDeclarationStatement)) {
            return null;
        }
        PsiClass psiClass = PsiTypesUtil.getPsiClass(psiLocal.getType());
        return psiClass;
    }

}
